﻿
using System;
using System.Linq;
using System.Security.Cryptography;

namespace Peer
{
    struct Handshake
    {
        /// <summary>
        /// 协议头
        /// </summary>
        public static readonly byte[] PROTOCOL_HEADER = { 0x4D, 0x4D, 0x4D };

        /// <summary>
        /// 握手包长度
        /// </summary>
        public const int HANDSHAKE_LENGTH = 3 + 16 + 4 + 16;

        /// <summary>
        /// 协议长度 guid16 ， 包体大小4 ， 包索引2 包类型 1 
        /// </summary>
        public const int HEADER_LENGTH = 16 + 4 + 2 + 1;

        public Handshake(byte[] bytes)
        {
            this.Bytes = bytes;
        }

        public static Handshake Empty = new Handshake();

        public byte[] Bytes { get; set; }

        public bool Verify(byte[] cipher)
        {
            if (this.Equals(Empty)) return false;
            var newCipher = Bytes.Skip(23).ToArray();
            return MD5Sign(Bytes.Take(23).ToArray(), cipher).SequenceEqual(newCipher);
        }

        public Guid Id => new Guid(Bytes.Skip(3).Take(16).ToArray());

        public uint Timestamp => BitConverter.ToUInt32(Bytes.Skip(19).Take(5).ToArray());

        public static Handshake Parse(byte[] bytes, int length)
        {
            if (length != HANDSHAKE_LENGTH)
                return Handshake.Empty;
            return new Handshake(bytes.Take(length).ToArray());
        }
        public static Handshake NewHandshake(byte[] cipher, Guid guid)
        {
            byte[] bytes = new byte[HANDSHAKE_LENGTH];
            var uuidBytes = guid.ToByteArray();
            var timeBytes = BitConverter.GetBytes((uint)DateTimeOffset.Now.ToUnixTimeSeconds());
            Array.Copy(PROTOCOL_HEADER, bytes, PROTOCOL_HEADER.Length);
            Array.Copy(uuidBytes, 0, bytes, 3, uuidBytes.Length);
            Array.Copy(timeBytes, 0, bytes, 3 + 16, timeBytes.Length);
            byte[] sign = MD5Sign(bytes.Take(3 + 16 + 4).ToArray(), cipher);
            Array.Copy(sign, 0, bytes, 3 + 16 + 4, sign.Length);
            return new Handshake(bytes);
        }

        public static Handshake NewHandshake(byte[] cipher)
        {
            return NewHandshake(cipher, Guid.NewGuid());
        }


        private static byte[] MD5Sign(byte[] data, byte[] cipher)
        {
            var sign = new byte[data.Length + cipher.Length];
            Array.Copy(data, sign, data.Length);
            Array.Copy(cipher, 0, sign, data.Length, cipher.Length);
            using (MD5 md5 = MD5.Create())
            {
                return md5.ComputeHash(sign);
            }
        }

    }
}
